using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;

namespace IppServer.Host.Tools
{
    /// <summary>
    /// IPP 客户端诊断工具
    /// 用于在客户端机器上运行，诊断 IPP 打印问题
    /// </summary>
    public static class IppClientDiagnostic
    {
        public static async Task<string> DiagnosePrinterAsync(string printerUri)
        {
            var report = new StringBuilder();
            report.AppendLine($"IPP 打印机诊断报告");
            report.AppendLine($"时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
            report.AppendLine($"打印机 URI: {printerUri}");
            report.AppendLine();

            try
            {
                // 1. 检查网络连接
                report.AppendLine("=== 网络连接检查 ===");
                var uri = new Uri(printerUri);
                var host = uri.Host;
                var port = uri.Port;

                try
                {
                    var testUrl = $"http://{host}:{port}";
                    var request = (HttpWebRequest)WebRequest.Create(testUrl);
                    request.Timeout = 5000;
                    request.Method = "GET";
                    using (var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(
                        request.BeginGetResponse, request.EndGetResponse, null))
                    {
                        report.AppendLine($"✓ HTTP 连接成功: {testUrl} (状态码: {response.StatusCode})");
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ HTTP 连接失败: {ex.Message}");
                }
                report.AppendLine();

                // 2. 发送 Get-Printer-Attributes 请求
                report.AppendLine("=== Get-Printer-Attributes 请求 ===");
                var ippRequest = BuildGetPrinterAttributesRequest(printerUri);
                var requestBytes = ippRequest;

                try
                {
                    var request = (HttpWebRequest)WebRequest.Create(printerUri);
                    request.Timeout = 10000;
                    request.Method = "POST";
                    request.ContentType = "application/ipp";
                    request.ContentLength = requestBytes.Length;
                    
                    using (var requestStream = await Task.Factory.FromAsync<Stream>(
                        request.BeginGetRequestStream, request.EndGetRequestStream, null))
                    {
                        await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);
                    }
                    
                    using (var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(
                        request.BeginGetResponse, request.EndGetResponse, null))
                    {
                        report.AppendLine($"✓ 请求已发送，状态码: {response.StatusCode}");
                        
                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            using (var responseStream = response.GetResponseStream())
                            using (var memoryStream = new MemoryStream())
                            {
                                await responseStream.CopyToAsync(memoryStream);
                                var responseBytes = memoryStream.ToArray();
                                report.AppendLine($"✓ 收到响应，大小: {responseBytes.Length} 字节");
                                report.AppendLine($"响应前64字节: {BitConverter.ToString(responseBytes, 0, Math.Min(64, responseBytes.Length)).Replace("-", " ")}");
                                
                                // 尝试解析响应
                                ParseIppResponse(responseBytes, report);
                            }
                        }
                        else
                        {
                            report.AppendLine($"✗ 请求失败: {response.StatusCode} {response.StatusDescription}");
                            using (var responseStream = response.GetResponseStream())
                            using (var reader = new StreamReader(responseStream))
                            {
                                var errorContent = await reader.ReadToEndAsync();
                                report.AppendLine($"错误内容: {errorContent}");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 请求异常: {ex.Message}");
                    report.AppendLine($"异常类型: {ex.GetType().Name}");
                    report.AppendLine($"堆栈跟踪: {ex.StackTrace}");
                }
                report.AppendLine();

                // 3. 检查 Windows 打印后台处理程序
                report.AppendLine("=== Windows 打印后台处理程序检查 ===");
                try
                {
                    var spoolerService = ServiceController.GetServices()
                        .FirstOrDefault(s => s.ServiceName == "Spooler");
                    if (spoolerService != null)
                    {
                        report.AppendLine($"✓ Spooler 服务状态: {spoolerService.Status}");
                    }
                    else
                    {
                        report.AppendLine($"✗ 未找到 Spooler 服务");
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 检查失败: {ex.Message}");
                }
                report.AppendLine();

                // 4. 检查已安装的打印机
                report.AppendLine("=== 已安装的打印机 ===");
                try
                {
                    foreach (string printerName in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
                    {
                        report.AppendLine($"  - {printerName}");
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 检查失败: {ex.Message}");
                }
                report.AppendLine();

            }
            catch (Exception ex)
            {
                report.AppendLine($"✗ 诊断过程发生异常: {ex.Message}");
                report.AppendLine($"异常类型: {ex.GetType().Name}");
                report.AppendLine($"堆栈跟踪: {ex.StackTrace}");
            }

            return report.ToString();
        }

        private static byte[] BuildGetPrinterAttributesRequest(string printerUri)
        {
            using (var stream = new MemoryStream())
            {
                // IPP 版本 2.0
                stream.WriteByte(0x02);
                stream.WriteByte(0x00);

                // 操作代码: Get-Printer-Attributes (0x000B)
                stream.WriteByte(0x00);
                stream.WriteByte(0x0B);

                // 请求 ID
                var requestId = 1;
                stream.WriteByte((byte)((requestId >> 24) & 0xFF));
                stream.WriteByte((byte)((requestId >> 16) & 0xFF));
                stream.WriteByte((byte)((requestId >> 8) & 0xFF));
                stream.WriteByte((byte)(requestId & 0xFF));

                // 操作属性组 (0x01)
                stream.WriteByte(0x01);

                // attributes-charset
                WriteStringAttribute(stream, 0x47, "attributes-charset", "utf-8");

                // attributes-natural-language
                WriteStringAttribute(stream, 0x48, "attributes-natural-language", "en-us");

                // printer-uri
                WriteStringAttribute(stream, 0x45, "printer-uri", printerUri);

                // 结束标记 (0x03)
                stream.WriteByte(0x03);

                return stream.ToArray();
            }
        }

        private static void WriteStringAttribute(Stream stream, byte tag, string name, string value)
        {
            var nameBytes = Encoding.ASCII.GetBytes(name);
            var valueBytes = Encoding.UTF8.GetBytes(value);

            stream.WriteByte(tag);
            stream.WriteByte((byte)(nameBytes.Length >> 8));
            stream.WriteByte((byte)(nameBytes.Length & 0xFF));
            stream.Write(nameBytes, 0, nameBytes.Length);
            stream.WriteByte((byte)(valueBytes.Length >> 8));
            stream.WriteByte((byte)(valueBytes.Length & 0xFF));
            stream.Write(valueBytes, 0, valueBytes.Length);
        }

        private static void ParseIppResponse(byte[] response, StringBuilder report)
        {
            if (response.Length < 8)
            {
                report.AppendLine("✗ 响应太短，无法解析");
                return;
            }

            try
            {
                var versionMajor = response[0];
                var versionMinor = response[1];
                var statusCode = (response[2] << 8) | response[3];
                var requestId = (response[4] << 24) | (response[5] << 16) | (response[6] << 8) | response[7];

                report.AppendLine($"IPP 版本: {versionMajor}.{versionMinor}");
                report.AppendLine($"状态码: 0x{statusCode:X4} ({GetStatusCodeName(statusCode)})");
                report.AppendLine($"请求 ID: {requestId}");

                // 简单的属性解析（仅用于诊断）
                var offset = 8;
                while (offset < response.Length)
                {
                    var tag = response[offset];
                    if (tag == 0x03) // 结束标记
                        break;

                    offset++;
                    if (offset >= response.Length) break;

                    var nameLength = (response[offset] << 8) | response[offset + 1];
                    offset += 2;
                    if (offset + nameLength > response.Length) break;

                    var name = nameLength > 0 ? Encoding.ASCII.GetString(response, offset, nameLength) : "";
                    offset += nameLength;
                    if (offset + 2 > response.Length) break;

                    var valueLength = (response[offset] << 8) | response[offset + 1];
                    offset += 2;
                    if (offset + valueLength > response.Length) break;

                    if (nameLength > 0 && valueLength > 0 && valueLength < 256)
                    {
                        var value = Encoding.UTF8.GetString(response, offset, valueLength);
                        if (name == "printer-name" || name == "printer-uri-supported" || 
                            name == "operations-supported" || name == "printer-is-accepting-jobs" ||
                            name == "document-format-supported" || name == "document-format-preferred")
                        {
                            report.AppendLine($"  {name} = {value}");
                        }
                    }
                    offset += valueLength;
                }
            }
            catch (Exception ex)
            {
                report.AppendLine($"✗ 解析响应时出错: {ex.Message}");
            }
        }

        private static string GetStatusCodeName(int statusCode)
        {
            switch (statusCode)
            {
                case 0x0000: return "successful-ok";
                case 0x0001: return "successful-ok-ignored-or-substituted-attributes";
                case 0x0400: return "client-error-bad-request";
                case 0x0401: return "client-error-forbidden";
                case 0x0402: return "client-error-not-authenticated";
                case 0x0403: return "client-error-not-authorized";
                case 0x0404: return "client-error-not-possible";
                case 0x0405: return "client-error-timeout";
                case 0x0406: return "client-error-not-found";
                case 0x0409: return "client-error-gone";
                case 0x040A: return "client-error-request-entity-too-large";
                case 0x040B: return "client-error-request-value-too-long";
                case 0x040C: return "client-error-document-format-not-supported";
                case 0x040D: return "client-error-attributes-or-values-not-supported";
                case 0x040E: return "client-error-uri-scheme-not-supported";
                case 0x040F: return "client-error-charset-not-supported";
                case 0x0410: return "client-error-compression-not-supported";
                case 0x0411: return "client-error-compression-error";
                case 0x0412: return "client-error-document-format-error";
                case 0x0413: return "client-error-document-access-error";
                default: return $"unknown-{statusCode:X4}";
            }
        }
    }
}

